Skip to content

Ansible Tutorial

Wir können Ansible mit Python pip installieren oder aber wie hier beschrieben aus den Debian Paketen.

sudo apt -y update
sudo apt -y install ansible

Nach der Installation können wir testen, ob Ansible erfolgreich installiert wurde.

ansible --version
ansible 2.9.6
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/mschulz/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.8.10 (default, Nov 14 2022, 12:59:47) [GCC 9.4.0]

Als nächstes erstellen wir einen SSH Key für Ansible mit ssh-keygen. Da dies ein Maschinen SSH Key wird, geben wir keine Passphrase ein.

ssh-keygen -t rsa -b 2048 -f ~/.ssh/ansible
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/tux/.ssh/ansible
Your public key has been saved in /home/tux/.ssh/ansible.pub
The key fingerprint is:
SHA256:qJIaMNOHebagIblGeKdSvb2NihH5sHwnIANb12g6Qng tux@earth
The key's randomart image is:
+---[RSA 2048]----+
|                 |
|.    o           |
|o.E + .          |
|+* O   .         |
|&.# * . S        |
|+& / =           |
|=.B B o          |
|.+ = o +         |
|. . ..o .        |
+----[SHA256]-----+

Nun kopieren wir den SSH Public key auf die entfernten Maschinen, welche von Ansible verwaltet werden sollen.

ssh-copy-id -i ~/.ssh/ansible.pub ansible@<SERVER>
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/tux/.ssh/ansible.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'ansible@<SERVER>'"
and check to make sure that only the key(s) you wanted were added.

Jetzt erstellen wir uns ein Git Repository für unser Ansible, wo wir die Konfiguration, wie auch die Playbooks speichern.

mkdir -p ~/ansible
cd ~/ansible/
git init

Nun erstellen wir eine inventory Datei, in dem wir die von Ansible verwalteten Maschinen eintragen.

vi inventory
192.168.0.3

Die Änderungen commiten wir nun in unseren Git Repository.

git add inventory
git commit -m "first version of the inventory file"
git push origin master

Nun testen wir unsere Ansible Konfiguration und setzen auf allen in der Inventory definierten Maschinen einen Ping ab.

ansible all --key-file ~/.ssh/ansible -i inventory -m ping
[WARNING]: Platform linux on host 192.168.0.3 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
192.168.0.3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

Der ping war erfolgreich, aber wir bekommen eine Warnung angezeigt. Um diese Warnung nicht mehr anzuzeigen, erstellen wir uns eine ansible.cfg im Git Repository und tragen unter [defaults] folgendes ein.

vi ansible.cfg

[defaults]

# See: https://stackoverflow.com/questions/70202432/getting-a-python-warning-when-running-playbook-ec2-inventory
interpreter_python = auto_silent

Nun aktualisieren wir unser Git Repository und testen die Konfiguration erneut.

git add ansible.cfg
git commit -m "ansible configuration added"
git push origin master

Nun prüfen wir die neue Konfiguration erneut.

ansible all --key-file ~/.ssh/ansible -i inventory -m ping
192.168.0.3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

Damit wir nicht jedes mal den Pfad zum Key wie auch zum Inventory angeben müssen, tragen wir auch dies in die ansible.cfg Konfiguration unter [defaults] ein.

vi ansible.cfg
[defaults]

# See: https://stackoverflow.com/questions/70202432/getting-a-python-warning-when-running-playbook-ec2-inventory
interpreter_python = auto_silent

inventory = inventory
private_key_file = ~/.ssh/ansible

Nun aktualisieren wir unser Git Repository und testen die Konfiguration erneut.

git add ansible.cfg
git commit -m "ansible configuration extended"
git push origin master

Die neue Konfiguration werden wir wie folgt testen. Dabei müssen wir nun nicht mehr den SSH Key, wie auch das Inventory angeben.

ansible all -m ping
192.168.0.3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

Mit folgenden Befehl zeigen wir alle Maschinen aus der Inventory an.

ansible all --list-hosts
  hosts (1):
    192.168.0.3

Mit dem Modul gather_facts (-m gather_facts) können wir details aus den Maschinen des Invetroy anzeigen lassen. Mit --limit grenzen wir die Ausgabe auf den definierten Maschinen ein (Ausgab stark verkürzt).

ansible all -m gather_facts --limit 192.168.0.3
192.168.0.3 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "172.26.0.1",
            "192.168.0.3",
.....


    },
    "changed": false,
    "deprecations": [],
    "warnings": []
}

Jetzt werden wir auf all unseren Maschinen mit dem Apt Befehl den Repository Index aktualisieren. Damit Ansible auch den Index aktualisieren kann, müssen wir das Sudo Password eingeben. Damit ansible dies auch abfragt, geben wir die Argumente --become --ask-become-pass mit an.

ansible all -m apt -a update_cache=true --become --ask-become-pass
BECOME password: 

PLAY [all] ************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

TASK [update repository index] ****************************************************************************************************************************************************************************************************************
changed: [192.168.0.3]

TASK [install apache2 package] ****************************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

PLAY RECAP ************************************************************************************************************************************************************************************************************************************
192.168.0.3                : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

Nun installieren wir auf allen Maschinen vim und mit dem Argument state=latest sorgen wir dafür, das auch wenn vim schon installiert ist, vim auf die aktuellese Version gehalten wird.

ansible all -m apt -a "name=vim state=latest" --become --ask-become-pass
BECOME password: 
192.168.0.3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "cache_update_time": 1672069465,
    "cache_updated": false,
    "changed": false
}

In diesem Fall ist auf allen Maschinen bereits vim installiert und auf dem neusten Stand.

Mit folgendem Befehl upgrade=dist halten wir die Maschinen auf den aktuellsten Stand.

ansible all -m apt -a "upgrade=dist" --become --ask-become-pass
BECOME password: 
192.168.0.3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "msg": "Reading package lists...\nBuilding dependency tree...\nReading state information...\nCalculating upgrade...\n0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.\n",
    "stderr": "",
    "stderr_lines": [],
    "stdout": "Reading package lists...\nBuilding dependency tree...\nReading state information...\nCalculating upgrade...\n0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.\n",
    "stdout_lines": [
        "Reading package lists...",
        "Building dependency tree...",
        "Reading state information...",
        "Calculating upgrade...",
        "0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded."
    ]
}

Damit wir nicht alles in der Befehlszeile eingeben müssen, erstellen wir uns unser ersten Playbook. In diesem definieren wir, dass der Packet Index aktualisiert wird und apache2 auf dem Server installiert wird, bzw. apache2 auf den neusten Stand gehalten wird. Die Notation wird im YAML Format erstellt.

vi install_apache2.yml
---

- hosts: all
  become: true
  tasks:

  - name: update repository index
    apt:
      update_cache: yes
    when: ansible_distribution in ["Debian", "Ubuntu"]

  - name: install apache2 package
    apt:
      name: apache2
      state: latest
    when: ansible_distribution in ["Debian", "Ubuntu"]

Möchte man den apache2 deinstallieren, können wir folgendes Playbook erstellen.

vi remove_apache2.yml
---

- hosts: all
  become: true
  tasks:

  - name: update repository index
    apt:
      update_cache: yes
    when: ansible_distribution in ["Debian", "Ubuntu"]

  - name: remove apache2 package
    apt:
      name: apache2
      state: absent
    when: ansible_distribution in ["Debian", "Ubuntu"]

Nun aktualisieren wir unser Git Repository und testen die Konfiguration erneut.

git add .
git commit -m "added install/remove playbooks for apache2"
git push origin master

Nun testen wir unser ersten Playbook, zum installieren des apache2.

ansible-playbook --ask-become-pass install_apache2.yml
BECOME password: 

PLAY [all] ************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

TASK [update repository index] ****************************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

TASK [install apache2 package] ****************************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

PLAY RECAP ************************************************************************************************************************************************************************************************************************************
192.168.0.3                : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Nun haben wir viele Abhängigkeiten und viel Code im Playbook. Wir werden daher das Playbook generisch abändern. Wir nutzen dann statt das apt Modul, das package Modul und statt der Package Namen wie apache2, verwenden wir Variablen, die wir im Inventory definieren werden. Somit kann das Playbook auch für z.B. CentOS verwendet werden.

Als erstes ändern wir unser Playbook wie folg ab.

vi install_apache2.yml
---

- hosts: all
  become: true
  tasks:

  - name: install apache and fcgid packages
    package:
      name:
        - "{{ apache_package }}"
        - "{{ fcgid_package }}"
      state: latest
      update_cache: yes

Die Variablen für die Package Namen definieren wir in unser inventory.

vi inventory
192.168.0.3 apache_package=apache2 fcgid_package=libapache2-mod-fcgid

Nun testen wir unsere Konfiguration.

ansible-playbook --ask-become-pass install_apache2.yml 
BECOME password: 

PLAY [all] ************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

TASK [install apache and fcgid packages] ******************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

PLAY RECAP ************************************************************************************************************************************************************************************************************************************
192.168.0.3                : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Würden wir im inventory z.B. ein RedHat definiert haben, würde dieses Playbook auch dafür funktionieren. Wir müssten nur apache2 mit httpd und libapache2-mod-fcgid mit mod_fcgid ändern bzw. im inventory definieren.

Nun aktualisieren wir unser Git Repository damit dieses wieder auf dem neusten Stand ist.

git add .
git commit -m "refactoring the playbook/inventory"
git push origin master

Nun werden wir in unseren inventory Gruppen hinzufügen. Ein Server kann auch in mehrere Gruppen sein, wie dies in meinem Fall ist.

vi inventroy
[web_servers]
192.168.0.3 apache_package=apache2 fcgid_package=libapache2-mod-fcgid
[db_servers]
192.168.0.3 mysql_package=default-mysql-server

Ausserdem haben wir in unserem Playbook noch tags gesetzt.

vi site.yml
---

- hosts: all
  become: true
  pre_tasks:

  - name: install updates (Debian)
    tags: always
    apt:
      upgrade: dist
      update_cache: yes
    when: ansible_distribution == "Debian"


- hosts: web_servers
  become: true
  tasks:

  - name: install apache and fcgid packages
    tags: apache,apache2
    package:
      name:
        - "{{ apache_package }}"
        - "{{ fcgid_package }}"
      state: latest


- hosts: db_servers
  become: true
  tasks:

  - name: install mysql-server packages
    tags: mysql,mariadb
    package:
      name:
        - "{{ mysql_package }}"
      state: latest

Mit dem nun folgenden Befehl, können wir uns alle Tags des jeweiligen Playbook anzeigen lassen.

ansible-playbook --list-tags site.yml
playbook: site.yml

  play #1 (all): all    TAGS: []
      TASK TAGS: [always]

  play #2 (web_servers): web_servers    TAGS: []
      TASK TAGS: [apache, apache2]

  play #3 (db_servers): db_servers  TAGS: []
      TASK TAGS: [mariadb, mysql]

Nun wird es Zeit, das neue Playbook mit zwei tags zu testen.

ansible-playbook --tags "apache2,mysql" --ask-become-pass site.yml
BECOME password: 

PLAY [all] ************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

TASK [install updates (Debian)] ***************************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

PLAY [web_servers] ****************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

TASK [install apache and fcgid packages] ******************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

PLAY [db_servers] *****************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

TASK [install mysql-server packages] **********************************************************************************************************************************************************************************************************
ok: [192.168.0.3]

PLAY RECAP ************************************************************************************************************************************************************************************************************************************
192.168.0.3                : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Dokumentation